/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * MainFrame.java
 *
 * Created on 2009-3-5, 1:08:22
 */
package cn.icybear.tools.ipclient4j.gui;

import cn.icybear.tools.ipclient4j.IPClient4J;
import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.Date;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JEditorPane;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.BadLocationException;

/**
 *
 * @author root
 */
public class MainFrame extends javax.swing.JFrame {

    private static final Image FRAME_ICO;
    public static final Logger LOG = Logger.getLogger("IPClient4J-GUI");
    private static final int MAX_LOG_COUNT = 10;
    private int logCount;
    private Preferences pref = Preferences.userNodeForPackage(getClass());


    static {
        try {
            FRAME_ICO = ImageIO.read(MainFrame.class.getResource("icon.bmp"));
        } catch (IOException ex) {
            throw new ExceptionInInitializerError(ex);
        }
    }
    private IPClient4J client;
    private Timer keepAliveTimer = new Timer(30 * 100, new ActionListener() {

        public void actionPerformed(ActionEvent e) {
            doKeepAlive();
        }
    });

    /** Creates new form MainFrame */
    public MainFrame() {
        initComponents();
        Handler logHandler = new Handler() {

            @Override
            public void publish(LogRecord record) {
                String message = String.format("%tT %s %s%n[%-5s]%s %s\n",
                        new Date(record.getMillis()),
                        record.getSourceClassName(),
                        record.getSourceMethodName(),
                        record.getLevel().getName(),
                        record.getMessage(),
                        record.getThrown() == null ? "" : record.getThrown());
//                if (record.getThrown() != null) {
//                    StringWriter sw = new StringWriter();
//                    record.getThrown().printStackTrace(new PrintWriter(sw));
//                    sw.append('\n');
//                    message += sw.toString();
//                }
                writeLog(message);
            }

            @Override
            public void flush() {
            }

            @Override
            public void close() throws SecurityException {
            }
        };
        txtUser.setText(pref.get("user", ""));
        txtPass.setText(pref.get("pass", ""));
        chkRemember.setSelected(pref.getBoolean("remember", false));
        IPClient4J.LOG.addHandler(logHandler);
        LOG.addHandler(logHandler);
        try {
            client = new IPClient4J("172.16.12.11");
            updateCodeImage();
        } catch (IOException ex) {
            reportError(ex);
            System.exit(-1);
        }
    }

    public synchronized void writeLog(String msg) {
        if (logCount++ == MAX_LOG_COUNT) {
            txtLog.setText("");
            logCount = 0;
        }
        try {
            txtLog.getDocument().insertString(0, msg, null);
            txtLog.select(0, 0);
        } catch (BadLocationException ex) {
            assert (false);//impossible
        }
    }

    public void updateCodeImage() throws IOException {
        lblCode.setIcon(new ImageIcon(client.getCodeImage()));
    }

    public void reportError(Throwable t) {
        LOG.log(Level.SEVERE, null, t);
        JOptionPane.showMessageDialog(rootPane, t.getMessage().equals("") ? t : t.getClass().getSimpleName() + ":" + t.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
    }

    public void reportError(Throwable t, String message) {
        LOG.log(Level.SEVERE, message, t);
        JOptionPane.showMessageDialog(rootPane, message + "\n" + t, "Error", JOptionPane.ERROR_MESSAGE);
    }
    private boolean locked;

    void lockControls(boolean lock) {
        locked = lock;
        txtCode.setEnabled(!lock);
        txtUser.setEnabled(!lock);
        txtPass.setEnabled(!lock);
        lblCode.setEnabled(!lock);
        chkRemember.setEnabled(!lock);
        if (lock) {
            btnAction.setText("Logout");
        } else {
            btnAction.setText("Login");
        }
    }

    synchronized void doKeepAlive() {
        try {
            client.keepAlive();
            lblFee.setText(String.format("You have %.02f CNY", client.fee));
            writeLog(String.format(
                    "Used:%d bytes Fee: %.02f CNY Last Update: %s%n" +
                    "Speed: [IN/OUT] " +
                    "Total: %,d/%,d " +
                    "TCP: %,d/%,d " +
                    "UDP: %,d/%,d " +
                    "Packet: %,d/%,d%n",
                    client.used, client.fee, client.lastRenew,
                    client.spdIn, client.spdOut,
                    client.spdInTcp, client.spdOutTcp,
                    client.spdInUdp, client.spdOutUdp,
                    client.spdInPkt, client.spdOutPkt));
        } catch (IOException ex) {
            reportError(ex, "KeepAlive");
            client.reset();
            lockControls(false);
        }
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        dlgAbout = new javax.swing.JDialog();
        jButton1 = new javax.swing.JButton();
        jLabel4 = new javax.swing.JLabel();
        jLabel5 = new javax.swing.JLabel();
        jScrollPane2 = new javax.swing.JScrollPane();
        jEditorPane1 = new javax.swing.JEditorPane();
        jLabel1 = new javax.swing.JLabel();
        txtUser = new javax.swing.JTextField();
        jLabel2 = new javax.swing.JLabel();
        txtPass = new javax.swing.JPasswordField();
        jLabel3 = new javax.swing.JLabel();
        txtCode = new javax.swing.JTextField();
        btnAction = new javax.swing.JButton();
        jScrollPane1 = new javax.swing.JScrollPane();
        txtLog = new javax.swing.JTextArea();
        lblCode = new javax.swing.JLabel();
        btnOrz = new javax.swing.JButton();
        chkRemember = new javax.swing.JCheckBox();
        lblFee = new javax.swing.JLabel();

        dlgAbout.setTitle("About");
        dlgAbout.setMinimumSize(new java.awt.Dimension(400, 300));
        dlgAbout.setModal(true);
        dlgAbout.setResizable(false);

        jButton1.setText("OK");
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });

        jLabel4.setText(" ");

        jLabel5.setText(" ");

        jEditorPane1.setBackground(java.awt.SystemColor.control);
        jEditorPane1.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1));
        jEditorPane1.setContentType("text/html");
        jEditorPane1.setEditable(false);
        jEditorPane1.setText("<html>\n<body>\n<H1><center>IPClient4J GUI</center></H1>\n<center> Version 1.0b </center>\n<p align=\"center\">\nCopyright (C) Bearice, 2009. (<a href=\"http://blog.icybear.cn/\">http://blog.icybear.cn/</a>)\n</p>\n<p align=\"center\">\nJCookie Copyright (C) Sonal Bansal, 2001-2002.(<a href=\"http://jcookie.sourceforge.net/\">http://jcookie.sourceforge.net/</a>)\n</p>\n<p>For more information, please view <a href=\"http://code.google.com/p/ipclient4j/\">http://code.google.com/p/ipclient4j/</a>.</p>\n<p>\nThis program is free software; you can redistribute it and/or modify it \nunder the terms of the GNU General Public License as published by the Free\n Software Foundation; either version 2 of the License, or (at your option) any \nlater version.\n</p><p>\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY \nWARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A \nPARTICULAR PURPOSE.  See the GNU General Public License for more details.\n</p><p>\nYou should have received a copy of the GNU General Public License along with this \nprogram; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, \nFifth Floor, Boston, MA  02110-1301, USA.\n</p>\n</body>\n</html>\n");
        jEditorPane1.addHyperlinkListener(new HyperlinkListener() {

            public void hyperlinkUpdate(HyperlinkEvent e) {
                if (e.getEventType() == HyperlinkEvent.EventType.ENTERED) {
                    ((JEditorPane) e.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
                } else if (e.getEventType() == HyperlinkEvent.EventType.EXITED) {
                    ((JEditorPane) e.getSource()).setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
                } else if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
                    try {
                        Desktop.getDesktop().browse(e.getURL().toURI());
                    } catch (Exception ex) {
                        reportError(ex);
                    }
                }
            }
        });
        jScrollPane2.setViewportView(jEditorPane1);

        javax.swing.GroupLayout dlgAboutLayout = new javax.swing.GroupLayout(dlgAbout.getContentPane());
        dlgAbout.getContentPane().setLayout(dlgAboutLayout);
        dlgAboutLayout.setHorizontalGroup(
            dlgAboutLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, dlgAboutLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(dlgAboutLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(jScrollPane2, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 380, Short.MAX_VALUE)
                    .addGroup(dlgAboutLayout.createSequentialGroup()
                        .addComponent(jLabel4, javax.swing.GroupLayout.DEFAULT_SIZE, 161, Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jButton1)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jLabel5, javax.swing.GroupLayout.DEFAULT_SIZE, 164, Short.MAX_VALUE)))
                .addContainerGap())
        );
        dlgAboutLayout.setVerticalGroup(
            dlgAboutLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, dlgAboutLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 249, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(dlgAboutLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jLabel4, javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(jButton1, javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(jLabel5, javax.swing.GroupLayout.Alignment.TRAILING))
                .addContainerGap())
        );

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("IPClient4J");
        setIconImage(FRAME_ICO);
        setMinimumSize(new java.awt.Dimension(300, 180));

        jLabel1.setText("User Name:");

        txtUser.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                txtCodeFocusGained(evt);
            }
        });

        jLabel2.setText("Password :");

        txtPass.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                txtCodeFocusGained(evt);
            }
        });

        jLabel3.setText("Code:");

        txtCode.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                txtCodeFocusGained(evt);
            }
        });

        btnAction.setText("Login");
        btnAction.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnActionActionPerformed(evt);
            }
        });

        txtLog.setColumns(20);
        txtLog.setEditable(false);
        txtLog.setRows(5);
        jScrollPane1.setViewportView(txtLog);

        lblCode.setPreferredSize(new java.awt.Dimension(50, 20));
        lblCode.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                lblCodeMouseClicked(evt);
            }
        });

        btnOrz.setText("?");
        btnOrz.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnOrzActionPerformed(evt);
            }
        });

        chkRemember.setText("Remember me");
        chkRemember.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                chkRememberActionPerformed(evt);
            }
        });

        lblFee.setText(" ");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                            .addComponent(jLabel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(jLabel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(txtCode, javax.swing.GroupLayout.PREFERRED_SIZE, 66, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(lblCode, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(chkRemember))
                            .addComponent(txtUser, javax.swing.GroupLayout.DEFAULT_SIZE, 211, Short.MAX_VALUE)
                            .addComponent(txtPass, javax.swing.GroupLayout.DEFAULT_SIZE, 211, Short.MAX_VALUE)))
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(btnOrz)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(lblFee, javax.swing.GroupLayout.DEFAULT_SIZE, 143, Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addComponent(btnAction, javax.swing.GroupLayout.PREFERRED_SIZE, 83, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 281, Short.MAX_VALUE))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(txtUser, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel2)
                    .addComponent(txtPass, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jLabel3)
                            .addComponent(txtCode, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(btnOrz)
                            .addComponent(btnAction)
                            .addComponent(lblFee)))
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                        .addComponent(chkRemember)
                        .addComponent(lblCode, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 138, Short.MAX_VALUE)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void lblCodeMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_lblCodeMouseClicked
        try {
            updateCodeImage();
        } catch (IOException ex) {
            reportError(ex);
        }
    }//GEN-LAST:event_lblCodeMouseClicked

    private void btnActionActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnActionActionPerformed
        if (locked) {
            try {
                keepAliveTimer.stop();
                client.sendClose();
                LOG.info("Login done successfully.");
            } catch (Exception ex) {
                reportError(ex);
            }
            lockControls(false);
        } else {
            try {
                lockControls(true);

                client.sendLogin(txtUser.getText(), txtPass.getPassword(), txtCode.getText());
                LOG.info("Login done successfully.");
                doKeepAlive();
                keepAliveTimer.start();
            } catch (Exception ex) {
                reportError(ex);
                lockControls(false);
            }
        }
        try {
            updateCodeImage();
        } catch (IOException ex) {
            reportError(ex, "reload code");
        }
}//GEN-LAST:event_btnActionActionPerformed

    private void txtCodeFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtCodeFocusGained
        ((JTextField) evt.getSource()).selectAll();
    }//GEN-LAST:event_txtCodeFocusGained

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
        dlgAbout.setVisible(false);
    }//GEN-LAST:event_jButton1ActionPerformed

    private void btnOrzActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOrzActionPerformed
        dlgAbout.setLocationRelativeTo(this);
        dlgAbout.setVisible(true);
    }//GEN-LAST:event_btnOrzActionPerformed

    private void chkRememberActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chkRememberActionPerformed
        if (chkRemember.isSelected()) {
            pref.putBoolean("remember", true);
            pref.put("user", txtUser.getText());
            pref.put("pass", txtPass.getText());
        } else {
            pref.putBoolean("remember", false);
            pref.put("user", "");
            pref.put("pass", "");
        }
    }//GEN-LAST:event_chkRememberActionPerformed

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) throws Exception {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                MainFrame mf = new MainFrame();
                mf.setLocationRelativeTo(null);
                mf.setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton btnAction;
    private javax.swing.JButton btnOrz;
    private javax.swing.JCheckBox chkRemember;
    private javax.swing.JDialog dlgAbout;
    private javax.swing.JButton jButton1;
    private javax.swing.JEditorPane jEditorPane1;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JLabel lblCode;
    private javax.swing.JLabel lblFee;
    private javax.swing.JTextField txtCode;
    private javax.swing.JTextArea txtLog;
    private javax.swing.JPasswordField txtPass;
    private javax.swing.JTextField txtUser;
    // End of variables declaration//GEN-END:variables
}
